﻿#region Copyright notice and license
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.  All rights reserved.
// http://github.com/jskeet/dotnet-protobufs/
// Original C++/Java/Python code:
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;

namespace Google.ProtocolBuffers {
  /// <summary>
  /// Implementation of the non-generic IMessage interface as far as possible.
  /// </summary>
  public abstract class AbstractBuilderLite<TMessage, TBuilder> : IBuilderLite<TMessage, TBuilder>
    where TMessage : AbstractMessageLite<TMessage, TBuilder>
    where TBuilder : AbstractBuilderLite<TMessage, TBuilder> {

    protected abstract TBuilder ThisBuilder { get; }

    public abstract bool IsInitialized { get; }

    public abstract TBuilder Clear();

    public abstract TBuilder Clone();

    public abstract TMessage Build();

    public abstract TMessage BuildPartial();

    public abstract TBuilder MergeFrom(IMessageLite other);

    public abstract TBuilder MergeFrom(CodedInputStream input, ExtensionRegistry extensionRegistry);

    public abstract TMessage DefaultInstanceForType { get; }

    #region IBuilderLite<TMessage,TBuilder> Members

    public virtual TBuilder MergeFrom(CodedInputStream input) {
      return MergeFrom(input, ExtensionRegistry.CreateInstance());
    }

    public TBuilder MergeDelimitedFrom(Stream input) {
      return MergeDelimitedFrom(input, ExtensionRegistry.CreateInstance());
    }

    public TBuilder MergeDelimitedFrom(Stream input, ExtensionRegistry extensionRegistry) {
      int size = (int)CodedInputStream.ReadRawVarint32(input);
      Stream limitedStream = new LimitedInputStream(input, size);
      return MergeFrom(limitedStream, extensionRegistry);
    }

    public TBuilder MergeFrom(ByteString data) {
      return MergeFrom(data, ExtensionRegistry.CreateInstance());
    }

    public TBuilder MergeFrom(ByteString data, ExtensionRegistry extensionRegistry) {
      CodedInputStream input = data.CreateCodedInput();
      MergeFrom(input, extensionRegistry);
      input.CheckLastTagWas(0);
      return ThisBuilder;
    }

    public TBuilder MergeFrom(byte[] data) {
      CodedInputStream input = CodedInputStream.CreateInstance(data);
      MergeFrom(input);
      input.CheckLastTagWas(0);
      return ThisBuilder;
    }

    public TBuilder MergeFrom(byte[] data, ExtensionRegistry extensionRegistry) {
      CodedInputStream input = CodedInputStream.CreateInstance(data);
      MergeFrom(input, extensionRegistry);
      input.CheckLastTagWas(0);
      return ThisBuilder;
    }

    public TBuilder MergeFrom(Stream input) {
      CodedInputStream codedInput = CodedInputStream.CreateInstance(input);
      MergeFrom(codedInput);
      codedInput.CheckLastTagWas(0);
      return ThisBuilder;
    }

    public TBuilder MergeFrom(Stream input, ExtensionRegistry extensionRegistry) {
      CodedInputStream codedInput = CodedInputStream.CreateInstance(input);
      MergeFrom(codedInput, extensionRegistry);
      codedInput.CheckLastTagWas(0);
      return ThisBuilder;
    }

    #endregion
    #region Explicit definitions

    IBuilderLite IBuilderLite.WeakClear() {
      return Clear();
    }

    IBuilderLite IBuilderLite.WeakMergeFrom(IMessageLite message) {
      return MergeFrom(message);
    }

    IBuilderLite IBuilderLite.WeakMergeFrom(ByteString data) {
      return MergeFrom(data);
    }

    IBuilderLite IBuilderLite.WeakMergeFrom(ByteString data, ExtensionRegistry registry) {
      return MergeFrom(data, registry);
    }

    IBuilderLite IBuilderLite.WeakMergeFrom(CodedInputStream input) {
      return MergeFrom(input);
    }

    IBuilderLite IBuilderLite.WeakMergeFrom(CodedInputStream input, ExtensionRegistry registry) {
      return MergeFrom(input, registry);
    }

    IMessageLite IBuilderLite.WeakBuild() {
      return Build();
    }

    IMessageLite IBuilderLite.WeakBuildPartial() {
      return BuildPartial();
    }

    IBuilderLite IBuilderLite.WeakClone() {
      return Clone();
    }

    IMessageLite IBuilderLite.WeakDefaultInstanceForType {
      get { return DefaultInstanceForType; }
    }

    #endregion
    #region LimitedInputStream
    /// <summary>
    /// Stream implementation which proxies another stream, only allowing a certain amount
    /// of data to be read. Note that this is only used to read delimited streams, so it
    /// doesn't attempt to implement everything.
    /// </summary>
    private class LimitedInputStream : Stream {

      private readonly Stream proxied;
      private int bytesLeft;

      internal LimitedInputStream(Stream proxied, int size) {
        this.proxied = proxied;
        bytesLeft = size;
      }

      public override bool CanRead {
        get { return true; }
      }

      public override bool CanSeek {
        get { return false; }
      }

      public override bool CanWrite {
        get { return false; }
      }

      public override void Flush() {
      }

      public override long Length {
        get { throw new NotSupportedException(); }
      }

      public override long Position {
        get {
          throw new NotSupportedException();
        }
        set {
          throw new NotSupportedException();
        }
      }

      public override int Read(byte[] buffer, int offset, int count) {
        if (bytesLeft > 0) {
          int bytesRead = proxied.Read(buffer, offset, Math.Min(bytesLeft, count));
          bytesLeft -= bytesRead;
          return bytesRead;
        }
        return 0;
      }

      public override long Seek(long offset, SeekOrigin origin) {
        throw new NotSupportedException();
      }

      public override void SetLength(long value) {
        throw new NotSupportedException();
      }

      public override void Write(byte[] buffer, int offset, int count) {
        throw new NotSupportedException();
      }
    }
    #endregion
  }
}
